#include "nvs.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "time.h"
#include "config.h"
#include "api_map.h"

#include "timer_task.h"

#define CONFIG_KEY "timers"

static const char *const TAG = "timer_task";

#pragma pack(1)
static struct timer_status
{
    uint8_t new_day : 1;        // 标记今天新的一天
    uint8_t executed_today : 1; // 标记今天是否已执行
    uint8_t executed_start : 1; // 标记今天是否已执行开始动作
    uint8_t executed_end : 1;   // 标记今天是否已执行结束动作
} timers_status[MAX_TIMERS] = {0};
#pragma pack()

RTC_NOINIT_ATTR static uint32_t timer_executed;
bool sunset_sunrise_init = false;
static timer_task_t *timers = NULL;
static uint8_t timer_count = 0;

/* 日出时间 */
static timer_time_t sunrise = {.hour = 6, .minute = 0};
/* 日落时间 */
static timer_time_t sunset = {.hour = 19, .minute = 0};

extern uint8_t timer_update_tz_sunset_sunrise(timer_time_t *sunrise, timer_time_t *sunset);

// 保存定时器配置到NVS
bool timer_save()
{
    // 保存到NVS
    if (config_set(CONFIG_KEY, sizeof(timer_task_t) * timer_count, (uint8_t *)timers))
        return config_commit();
    return true;
}

// 从NVS加载定时器配置
bool timer_load()
{
    uint16_t len = config_get(CONFIG_KEY, NULL, 0);
    if (len > sizeof(timer_task_t) * MAX_TIMERS)
        len = sizeof(timer_task_t) * MAX_TIMERS;
    bool res = config_get(CONFIG_KEY, (uint8_t *)timers, len) == len;
    if (res)
        timer_count = len / sizeof(timer_task_t);
    return res;
}

bool timer_add(timer_task_t *config)
{
    if (!timers || timer_count >= MAX_TIMERS)
        return false;

    memcpy(&timers[timer_count], config, sizeof(timer_task_t));
    memset(&timers_status[timer_count], 0, sizeof(struct timer_status));
    timer_count++;

    return true;
}

timer_task_t *timer_get(uint8_t idx)
{
    if (!timers || idx >= MAX_TIMERS)
        return NULL;
    return &timers[idx];
}

uint8_t timer_get_len(void)
{
    return timer_count;
}

void timer_remove_all(void)
{
    memset(timers, 0, sizeof(timer_task_t) * MAX_TIMERS);
    memset(timers_status, 0, sizeof(timers_status));
    timer_count = 0;
    timer_executed = 0;
}
bool timer_remove(uint8_t idx)
{
    if (!timers || idx >= MAX_TIMERS)
        return false;

    if (timer_count - idx - 1)
    {
        memmove(&timers[idx], &timers[idx + 1], sizeof(timer_task_t) * (timer_count - idx - 1));
        memmove(&timers_status[idx], &timers_status[idx + 1], sizeof(struct timer_status) * (timer_count - idx - 1));
    }

    timer_count--;
    memset(&timers[timer_count], 0, sizeof(timer_task_t));
    memset(&timers_status[timer_count], 0, sizeof(struct timer_status));
    timer_executed &= ~(1 << (idx * 2 + 1));

    return true;
}

void timer_status_clean(uint8_t idx)
{
    if (!timers || idx >= MAX_TIMERS)
        return;
    memset(&timers_status[idx], 0, sizeof(struct timer_status));
    timer_executed = 0;
}

// 执行定时器动作
static void execute_timer_action(timer_task_t *timer, bool is_start, struct tm *timeinfo)
{
    timer_action_t action = is_start ? timer->start_action : timer->end_action;
    timer_time_t time = {0};

    if (timer->time_type == TIMER_SUNRISE_SUNET)
        time = is_start ? sunrise : sunset;
    else
        time = is_start ? timer->start : timer->end;

    ESP_LOGI(TAG, "Executing %s %s action: %s", timer->name, is_start ? "start" : "end", action.name);
    ESP_LOGI(TAG, "call %s(%d,%d) time %d:%d >= %d:%d ", action.name, action.handle, action.param, timeinfo->tm_hour, timeinfo->tm_min, time.hour, time.minute);

    int result = timer_action_call(action.name, action.handle, action.param);
    if (result == ACTION_RESULT_SUCCESS)
    {
        ESP_LOGI(TAG, "Action executed successfully");
    }
    else
    {
        ESP_LOGE(TAG, "Action execution failed");
    }
}

// 检查定时器是否应该触发
void timer_loop(void)
{
    if (!timer_count)
        return;
    time_t now;
    struct tm timeinfo;
    time(&now);
    localtime_r(&now, &timeinfo);

    uint16_t sec = timeinfo.tm_hour * 3600 + timeinfo.tm_min * 60 + timeinfo.tm_sec;
    uint8_t weekday_mask = 1 << timeinfo.tm_wday;

    for (int i = 0; i < timer_count; i++)
    {
        timer_task_t *timer = &timers[i];
        struct timer_status *timer_status = &timers_status[i];
        if (!timer->enabled)
            continue;

        if (timer_status->executed_today) /* 今天已执行了开始和结束动作 */
        {
            if (timer_status->new_day) /* 新的一天 */
            {
                timer_status->new_day = 0;
                timer_status->executed_start = false;
                timer_status->executed_end = false;
                timer_status->executed_today = false; /* 重新记录 */
            }
            continue;
        }

        // 如果是周定时器，则检查当前是周几, 不是则跳过
        if (timer->type == TIMER_WEEKLY && !(timer->weekdays & weekday_mask))
            continue;

        if (!timer_status->executed_start) // 检查是否未动作
        {
            // 检查是否超过了 开始 - 结束 时间范围
            timer_time_t time = timer->end;
            if (timer->time_type == TIMER_SUNRISE_SUNET)
                time = sunset;
            uint32_t cfg_sec = time.hour * 3600 + time.minute * 60;

            /* 越过了 开始 - 结束 区间, 且未执行开动作, 则跳过 */
            if (sec > cfg_sec)
                continue;

            bool should_trigger = false; /* 默认执行动作 */

            /* 判断是否处于  开始 - 结束 区间 内, 且未执行开始动作 */
            time = timer->start;
            if (timer->time_type == TIMER_SUNRISE_SUNET)
                time = sunrise;
            cfg_sec = time.hour * 3600 + time.minute * 60;

            if (sec >= cfg_sec)
                should_trigger = true; /* 默认执行动作 */

            if (should_trigger)
            {
                execute_timer_action(timer, true, &timeinfo);
                timer_status->executed_start = true;
                timer_executed |= (1 << (i * 2));
                goto check_timer_executed_end;
            }
        }
        else if (!timer_status->executed_end) // 只有执行过开始动作, 才会检查 结束 是否未动作
        {
        check_timer_executed_end:
            bool should_trigger = false; /* 默认执行动作 */

            timer_time_t time = timer->end;
            if (timer->time_type == TIMER_SUNRISE_SUNET) // 检查是否到达 结束 时间
                time = sunset;
            uint16_t cfg_sec = time.hour * 3600 + time.minute * 60;

            if (sec >= cfg_sec)
                should_trigger = true; /* 默认执行动作 */

            if (should_trigger)
            {
                execute_timer_action(timer, false, &timeinfo);
                timer_status->executed_end = true;
                timer_executed |= (1 << (i * 2 + 1));
            }
        }
        else /* 今天结束 */
        {
            if (timer->type == TIMER_ONCE)
                timer->enabled = false;
            timer_status->executed_today = true;
        }
    }
}

#define timer_json_put_str(key, value) len += snprintf(out + len, size - len, "\"" #key "\":\"%s\",", value)
#define timer_json_put_number(key, value) len += snprintf(out + len, size - len, "\"" #key "\":%d,", value)
#define timer_json_put_key_number(key, value) len += snprintf(out + len, size - len, "\"" #key "\":%d,", value)
#define timer_json_put_obj(name) len += snprintf(out + len, size - len, "\"" #name "\":{")
#define timer_json_put_obj_end()                      \
    do                                                \
    {                                                 \
        len--;                                        \
        len += snprintf(out + len, size - len, "},"); \
    } while (0)

uint16_t timer_task_to_json(timer_task_t *timer, char *out, uint16_t size)
{
    if (!timer || !out)
        return 0;
    uint16_t len = 0;
    /**
        {
            "name": "xxxxx",
            "enable": 1,
            "type": 0,
            "time_type": 0,
            "weekdays": 0,
            "start": {
                "hour": 6,
                "minute": 30,
                "action": {
                    "name": "xxxxx",
                    "handle": 0,
                    "param": 1,
                }
            },
            "end": {
                "hour": 19,
                "minute": 0,
                "action": {
                    "name": "xxxxx",
                    "handle": 0,
                    "param": 0,
                }
            }
        }
    */
    len += snprintf(out + len, size - len, "{");

    timer_json_put_str(name, timer->name);
    timer_json_put_number(enable, timer->enabled);
    timer_json_put_number(weekdays, timer->weekdays);
    timer_json_put_number(type, timer->type);
    timer_json_put_number(time_type, timer->time_type);

    timer_json_put_obj(start);
    timer_json_put_key_number(hour, timer->start.hour);
    timer_json_put_key_number(minute, timer->start.minute);
    timer_json_put_obj(action);
    timer_json_put_str(name, timer->start_action.name);
    timer_json_put_key_number(handle, timer->start_action.handle);
    timer_json_put_key_number(param, timer->start_action.param);
    timer_json_put_obj_end();
    timer_json_put_obj_end();

    timer_json_put_obj(end);
    timer_json_put_key_number(hour, timer->end.hour);
    timer_json_put_key_number(minute, timer->end.minute);
    timer_json_put_obj(action);
    timer_json_put_str(name, timer->end_action.name);
    timer_json_put_key_number(handle, timer->end_action.handle);
    timer_json_put_key_number(param, timer->end_action.param);
    timer_json_put_obj_end();
    timer_json_put_obj_end();

    len--;
    len += snprintf(out + len, size - len, "},");
    return len;
}

uint16_t timers_to_json(char *out, uint16_t size)
{
    uint16_t len = 0;
    len += snprintf(out + len, size - len, "[");

    uint16_t last_len = len;
    for (uint8_t i = 0; i < timer_count; i++)
        len += timer_task_to_json(&timers[i], out + len, size - len);

    if (last_len != len)
        len--;
    len += snprintf(out + len, size - len, "],");
    return len;
}

uint16_t sunrise_sunset_to_json(char *out, uint16_t size)
{
    uint16_t len = 0;
    len += snprintf(out + len, size - len, "{");

    timer_json_put_obj(sunrise);
    timer_json_put_key_number(hour, sunrise.hour);
    timer_json_put_key_number(min, sunrise.minute);
    timer_json_put_obj_end();

    timer_json_put_obj(sunset);
    timer_json_put_key_number(hour, sunset.hour);
    timer_json_put_key_number(min, sunset.minute);
    timer_json_put_obj_end();

    len--;
    len += snprintf(out + len, size - len, "},");
    return len;
}

void timer_task_print(timer_task_t *timer)
{
    static const char *const type2str[] = {"一次", "每天", "星期", "日期"};
    printf("timer : %s\t%s\r\n", timer->name, timer->enabled ? "enable" : "disable");
    printf("type : %d(%s)\ttime type : %d \r\n", timer->type, type2str[timer->type], timer->time_type);
    if (timer->type == TIMER_WEEKLY)
    {
        printf("日\t一\t二\t三\t四\t五\t六\r\n");
        for (uint8_t i = 0; i < 7; i++)
        {
            if ((timer->weekdays >> i) & 1)
                printf("√");
            printf("\t");
        }
        printf("\r\n");
        printf("S\tM\tT\tW\tT\tF\tS\r\n");
    }
    if (timer->time_type == TIMER_SUNRISE_SUNET)
        printf("time : %d:%d -> %d:%d (sunrise-sunset | 日出日落)\r\n", sunrise.hour, sunrise.minute, sunset.hour, sunset.minute);
    else
        printf("time : %d:%d -> %d:%d\r\n", timer->start.hour, timer->start.minute, timer->end.hour, timer->end.minute);
    printf("Start Action : %s\thandle : %d\tparam : %d\r\n", timer->start_action.name, timer->start_action.handle, timer->start_action.param);
    printf(" End Action  : %s\thandle : %d\tparam : %d\r\n", timer->end_action.name, timer->end_action.handle, timer->end_action.param);
    printf("\r\n");
}

void timer_task_print_all(void)
{
    printf("\r\n");
    for (uint8_t i = 0; i < timer_count; i++)
        timer_task_print(&timers[i]);
}

static void timer_thread(void *arg)
{
    timer_loop();
    if (!sunset_sunrise_init)
    {
        sunset_sunrise_init = true;
        timer_update_tz_sunset_sunrise(&sunrise, &sunset);
    }
}

void sunrise_sunset_update(void *arg)
{
    timer_update_tz_sunset_sunrise(&sunrise, &sunset);
}

void timer_api_init(void)
{
    esp_reset_reason_t reason = esp_reset_reason();
    if (reason == ESP_RST_POWERON)
        timer_executed = 0;
    timers = malloc(MAX_TIMERS * sizeof(timer_task_t));
    if (!timers)
        return;
    memset(timers, 0, MAX_TIMERS * sizeof(timer_task_t));
    timer_load();

    timer_task_print_all();

    for (uint8_t i = 0; i < timer_count; i++)
    {
        if ((timer_executed >> (i * 2)) & 1)
            timers_status[i].executed_start = 1;
        if ((timer_executed >> (i * 2 + 1)) & 1)
            timers_status[i].executed_end = 1;
    }

    {
        const esp_timer_create_args_t timer_timer_args = {
            .callback = &timer_thread,
            .name = "timer"};
        esp_timer_handle_t timer_timer = NULL;
        ESP_ERROR_CHECK(esp_timer_create(&timer_timer_args, &timer_timer));
        ESP_ERROR_CHECK(esp_timer_start_periodic(timer_timer, 60 * 1000 * 1000)); /* 1 minus */
    }

    {
        const esp_timer_create_args_t timer_timer_args = {
            .callback = &sunrise_sunset_update,
            .name = "suns_update"};
        esp_timer_handle_t timer_timer = NULL;
        ESP_ERROR_CHECK(esp_timer_create(&timer_timer_args, &timer_timer));
        ESP_ERROR_CHECK(esp_timer_start_periodic(timer_timer, 12 * 60 * 60 * 1000 * 1000)); /* 12 hour */
    }

    extern void timer_task_api_init();
    timer_task_api_init();
}
ADD_API(timer_api_init)
